import * as THREE from 'three';
// Orbit controls（轨道控制器）可以使得相机围绕目标进行轨道运动
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

// 导入物理引擎库
import * as CANNON from 'cannon'

/**
 * 为了真正能够让你的场景借助 three.js 来进行显示，
 * 需要以下几个对象：场景（scene）、相机（camera）和渲染器（renderer），
 * 就能透过摄像机渲染出场景。
 * */
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xBACDE0)
/**
 * PerspectiveCamera（透视摄像机）
 * 第一个参数是视野角度（FOV）
 *    视野角度就是无论在什么时候，你所能在显示器上看到的场景的范围，它的单位是角度(与弧度区分开)。
 * 第二个参数是长宽比（aspect ratio）
 *    用一个物体的宽除以它的高的值。比如说，当你在一个宽屏电视上播放老电影时，可以看到图像仿佛是被压扁的。
 * 接下来的两个参数是近截面（near）和远截面（far）
 *    当物体某些部分比摄像机的远截面远或者比近截面近的时候，该这些部分将不会被渲染到场景中。
 *    或许现在你不用担心这个值的影响，但未来为了获得更好的渲染性能，你将可以在你的应用程序里去设置它。
 */
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
// const helper = new THREE.CameraHelper( camera );
// scene.add( helper );
camera.position.set(0, 20, 130)
camera.lookAt(0,0,0)


/**
 * 接下来是渲染器。
 *    在这里用到的 WebGLRenderer 渲染器之外，Three.js 同时提供了其他几种渲染器，当用户所使用的浏览器过于老旧，或者由于其他原因不支持 WebGL 时，可以使用这几种渲染器进行降级。
 */
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#canvas'), //指定渲染的容器，不添加则会自动生成
  antialias: true, //抗锯齿 像素点
});
renderer.shadowMap.enabled = true


/**
 *    还需要在我们的应用程序里设置一个渲染器的尺寸。比如说，我们可以使用所需要的渲染区域的宽高，来让渲染器渲染出的场景填充满我们的应用程序。
 *    因此，我们可以将渲染器宽高设置为浏览器窗口宽高。
 *    对于性能比较敏感的应用程序来说，可以使用 setSize 传入一个较小的值，例如 window.innerWidth/2 和 window.innerHeight/2，这将使得应用程序在渲染时，以一半的长宽尺寸渲染场景。
 *    如果希望保持应用程序的尺寸，但是以较低的分辨率来渲染，可在调用 setSize 时，将 updateStyle（第三个参数）设为 false。
 *    例如，假设你的 <canvas> 标签现在已经具有了 100% 的宽和高，调用 setSize(window.innerWidth/2, window.innerHeight/2, false) 将使得你的应用程序以四分之一的大小来进行渲染。
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
// renderer.setSize( window.innerWidth, window.innerHeight ); //设置渲染器的大小
/**
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
// document.body.appendChild( renderer.domElement ); //则需要添加至页面

// 创建坐标网格辅助线
const size = 100;
const divisions = 10;

const gridHelper = new THREE.GridHelper( size, divisions, 0xff0000 );

// 使用纹理加载器二  使用循环渲染就无需再回调中重新渲染
const texture = new THREE.TextureLoader().load('./image/pandas.jpg')

// 创建方向光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(50, 100, 100);
scene.add(directionalLight);

// 创建结构  几何体--平面                
const plane = new THREE.Mesh(
  new THREE.PlaneGeometry(180,200),
  new THREE.MeshPhongMaterial({
    color: 0xe8e8e8,
  })
)
plane.rotation.x = -Math.PI / 2

// 创建结构  几何体--球体  
const radius = 10                      
const sphere = new THREE.Mesh( 
  new THREE.SphereGeometry(radius,32,32), 
  new THREE.MeshPhongMaterial({
    // color: 0xff0000,
    map: texture
  })
);
sphere.position.set(-20,40,20)

scene.add(plane, sphere)


// 创建物理世界
const world = new CANNON.World();
// 物理世界中的重力
world.gravity.set(0, -9.8, 0);
// 创建平面与小球的物理材料
 const groundMaterial = new CANNON.Material('groundMaterial');
 const sphereMaterial = new CANNON.Material('sphereMaterial');
// 定义接触材料
 const contactMaterial = new CANNON.ContactMaterial(groundMaterial, sphereMaterial, {
  friction: 0.5, //表示当这两种材质接触时，它们之间的摩擦力大小
  restitution: 0.8 //反弹系数 默认0.3
 });
 // 物理世界添加接触材料
 world.addContactMaterial(contactMaterial);
// 创建物理物体
// 创建物体物体-平面
 const planeShape = new CANNON.Plane();
//  创建物理刚体
 const planeBody = new CANNON.Body({ 
  mass: 0,
  // shape: new CANNON.Plane(),
  material: groundMaterial
 });
//  设置欧拉角 旋转
 planeBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
 planeBody.addShape(planeShape);
 world.addBody(planeBody);
// 创建物理物体-球体  
 const sphereShape = new CANNON.Sphere(radius);
 const sphereBody = new CANNON.Body({ 
  mass: 1,
  // position: new CANNON.Vec3(0,35,0),
  position: sphere.position,
  material: sphereMaterial,
  linearDamping: 0.6
 });
//  刚体添加形状
sphereBody.addShape(sphereShape);

// 添加水平方向的力 朝着指定方向施加一个力
sphereBody.applyLocalForce(
  new CANNON.Vec3(500, 0, 0), // 力的方向
  new CANNON.Vec3(0, 10, 0), // 力的作用点在刚体的局部坐标系中的位置
)
//  sphereBody.position.set(0, 35, 0);

 world.addBody(sphereBody);

//  更新
const updatePhysic = () => {
  // 物理世界中的物体运动  每秒60帧
  world.step(1 / 60);
  // 打印小球刚体的位置
  // console.log(sphereBody.position);
  sphere.position.copy(sphereBody.position);
  sphere.quaternion.copy(sphereBody.quaternion);
}


// 模拟3个坐标轴的对象.
// 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
const axesHelper = new THREE.AxesHelper(150);
// 设置x,y,z轴的颜色
axesHelper.setColors(new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color('pink'))


// scene.add( gridHelper, axesHelper );

// 控制器的使用
// 控制器实例化  参数：相机  绑定事件的元素
const controls = new OrbitControls( camera, renderer.domElement );

//controls.update() must be called after any manual changes to the camera's transform
controls.enableDamping = true //启用惯性


controls.addEventListener('change', () => {
  renderer.render( scene, camera )
  console.log(camera.position);
  
})

renderer.render( scene, camera );  //render  执行一次渲染一次
/**
 * 进行真正的渲染
 * 需要使用一个被叫做“渲染循环”（render loop）或者“动画循环”（animate loop）的东西。
 */
function animate() {
	requestAnimationFrame( animate );
  updatePhysic()
  controls.update();
	renderer.render( scene, camera );  //render  执行一次渲染一次
}
animate();
